package com.btm;

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 将系统的空闲内存控制在一个区间内，在“最小空闲内存百分比”与“最大空闲内存百分比”之间，当空闲内存小于最小空闲内存百分比且本身持有内存时则释放；
 * 当空闲内存大于最大空闲内存百分比且本身持有内存时则创建对象占用。</br>
 * 由于java的运行依赖与JVM，JVM的内存释放相对于程序中的对象清理有延迟，故此减缓内存释放的速度。</br>
 * 推荐：最小空闲内存百分比不小于15%，最大比最小要多出10个百分点。</br>
 */
public class MemoryManager {

    private final static int SIZE = 10;

    private volatile boolean running = false;

    private final List<byte[]> memoryHog = new ArrayList<>();

    public static void main(String[] args) {
        MemoryManager manager = new MemoryManager();
        if (args.length != 3) {
            System.out.println("需要指定三个参数：最小空闲内存百分比，最大空闲内存百分比，检测间隔时间（秒）");
            return;
        }
        try {
            manager.startMemoryManagement(Integer.parseInt(args[0]), Integer.parseInt(args[1]), Integer.parseInt(args[2]));
        } catch (NumberFormatException | InterruptedException e) {
            System.out.println("参数错误，请输入数字");
        }
    }


    public void startMemoryManagement(int minFreeMemory, int maxFreeMemory, int gap) throws InterruptedException {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(() -> {
            try {
                if (running) {
                    // 防止重复运行
                    return;
                }
                running = true;
                long freeMemory = getFreeSystemMemory();
                long totalMemory = getTotalSystemMemory();
                long freeMemoryPercent = (freeMemory * 100) / totalMemory;
                long runMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
                long runFreeMemory = Runtime.getRuntime().freeMemory() / 1024 / 1024;

                System.out.println("Run Memory: " + runMemory + "MB, Run-Free Memory : " + runFreeMemory + "MB, " +
                        "Free Memory: " + freeMemory + "MB, " +
                        "Total Memory: " + totalMemory + "MB, " + freeMemoryPercent + "%，" +
                        "Releasable Memory: " + (runMemory - runFreeMemory) + "MB");

                if (freeMemoryPercent > maxFreeMemory) {
                    // 空闲内存高于最高值，创建对象占用内存
                    for (int i = 0 ;i<20;i++) {
                        if (getFreeSystemMemory() * 100 / getTotalSystemMemory() > maxFreeMemory) {
                            allocateMemory();
                        } else {
                            break;
                        }
                    }
//                System.out.println("Allocated memory: " + businessMemory * 10 + "MB");
                } else if (freeMemoryPercent < minFreeMemory) {
                    // 空闲内存低于最低值，删除对象释放内存
                    for (int i = 0 ;i<20;i++) {
                        if (getFreeSystemMemory() * 100 / getTotalSystemMemory() < minFreeMemory) {
                            releaseMemory();
                        } else {
                            break;
                        }
                    }
//                System.out.println("Release memory: " + businessMemory * 10 + "MB");
                    System.gc();
                }
            } catch (Throwable e) {
                e.printStackTrace();
                memoryHog.clear();
                System.gc();
            } finally {
                running = false;
            }
        }, 0, gap, TimeUnit.SECONDS);
        synchronized (memoryHog) {
            memoryHog.wait();
        }
    }

    private long getFreeSystemMemory() {
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        if (osBean instanceof com.sun.management.OperatingSystemMXBean) {
            com.sun.management.OperatingSystemMXBean sunOsBean =
                    (com.sun.management.OperatingSystemMXBean) osBean;
            return (sunOsBean.getFreeSwapSpaceSize() + sunOsBean.getFreePhysicalMemorySize())  / 1024 / 1024;
        }
        throw new UnsupportedOperationException("Cannot get free system memory on this JVM.");
    }

    private long getTotalSystemMemory() {
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        if (osBean instanceof com.sun.management.OperatingSystemMXBean) {
            com.sun.management.OperatingSystemMXBean sunOsBean =
                    (com.sun.management.OperatingSystemMXBean) osBean;
            return sunOsBean.getTotalPhysicalMemorySize() / 1024 / 1024;
        }
        throw new UnsupportedOperationException("Cannot get total system memory on this JVM.");
    }

    private void allocateMemory() {
        // 创建一个10MB的对象来占用内存
        memoryHog.add(new byte[SIZE * 1024 * 1024]);
    }

    private void releaseMemory() {
        if (!memoryHog.isEmpty()) {
            // 删除一个对象释放内存
            memoryHog.remove(memoryHog.size() - 1);
        }
    }
}